/*
avi.c
*/
#include "avi.h"
#include "byteswap.h"
#include <stdio.h>
#include "avifmt.h"
#include <string.h>

#include <sys/types.h>
//#include <sys/param.h>
//#include <unistd.h>

#include <sys/stat.h>
#include <windows.h>
#include <fcntl.h>
#include <stdlib.h>
#include "llist.h"

//void print_quartet(unsigned int i)
void print_quartet(unsigned int i, FILE * avifd)
{
  fprintf(avifd,"%c",i % 0x100); i /= 0x100;
  fprintf(avifd,"%c",i % 0x100); i /= 0x100;
  fprintf(avifd,"%c",i % 0x100); i /= 0x100;
  fprintf(avifd,"%c",i % 0x100);
}

FILE * avi_open(const char *filename,const char *mode)
{
    
	FILE * ret; 

	/*Create a empty avi file*/
	if ((ret = fopen(filename, mode)) == NULL) {
      fprintf(stderr, "couldn't open file!\n");
      return NULL;
    }

	return ret;
} 

int avi_write(FILE * avifile, Avi_Data *avi_data, char * jpg_buff,unsigned int jpg_length)
{
	int ret = 0;

	unsigned int jpg_length_align;
	Jpeg_Data *tmp;
	off_t mfsz, remnant;
	off64_t riff_sz_64 = 0;

//	if(avi_data->frame_sum > MAX_FRAME_SUM - 1)
//	{
//		/*if write too many frames, return*/
//		//printf("\nWARNing: will exceed MAX_FRAME_SUM ,frame_sum=%d",frame_sum);
//		return -1;
//	}
	
	//2005-11-09
	//riff_sz_64 = sizeof(struct AVI_list_hdrl) + 4 + 4 + jpg_sz_64
	//				+ 8*frame_sum + 8 + 8 + 16*frame_sum;
	riff_sz_64 = sizeof(struct AVI_list_hdrl) + 4 + 4 + avi_data->jpg_sz_64
				+ 8*avi_data->frame_sum + 8 + 8 + 16*avi_data->frame_sum;


	
//	//prevent from exceed (2GB-10MB)
//	if (riff_sz_64 >= MAX_RIFF_SZ) {
//
//	/*
//    if (riff_sz_64 + 10485760 >= MAX_RIFF_SZ ) 
//	{
//		//fprintf(stderr,"\nRIFF would exceed 2 Gb limit\n");
//		//printf("\nframe_sum=%d,riff_sz_64=%d",frame_sum,riff_sz_64);
//		return -1;
//	}
//	*/

	/*Create frame list head node*/
	if(avi_data->frame_sum == 0)
	{
		avi_data->frlst = (List *) malloc(sizeof(List));
		memset(avi_data->frlst,0,sizeof(List));
		avi_data->frlst_back = avi_data->frlst;
			
		/* 1st frame should begin from 0xf0 of avi file
		avi head would be written after all the frame have been written into avi file
		*/
		fseek(avifile, 0xf0, SEEK_SET);
    }

    /*start of writing a frame into avi file*/
    if (fwrite("00db", 4, 1, avifile) != 1)
		 ret = -2;

	if((tmp = (Jpeg_Data *) malloc(sizeof(Jpeg_Data)))==NULL)
	{
	    fprintf(stderr, "couldn't malloc buffer  !\n");
		return -3;	
	}
    //printf("\n!Ok mallock a Jpeg_Data");

	//get the 4 bytes aligned jpg file's length	
	mfsz = jpg_length;
    remnant = (4-(mfsz%4)) % 4;
    print_quartet(mfsz + remnant,avifile);
    tmp->size = mfsz + remnant;
	jpg_length_align = mfsz + remnant;

	//insert data to the end of frame info list
	if (avi_data->frlst_back->data == 0)
	{
		avi_data->frlst_back->data = tmp;
	}
	else
	{
		//f = list_push_back(frlst, tmp);
		avi_data->frlst_back = list_push_back(avi_data->frlst_back,tmp);
    }
	//printf("\nOk! insert tmp to the end of the frame list");

    //if (f == frlst) {
	if(avi_data->frame_sum==0) 
	{
      ((Jpeg_Data *) avi_data->frlst_back->data)->offset = 4;

    }
	else 
	{
      ((Jpeg_Data *) avi_data->frlst_back->data)->offset = 
		((Jpeg_Data *) avi_data->frlst_back->prev->data)->offset +
		((Jpeg_Data *) avi_data->frlst_back->prev->data)->size + 8;
    }
    
	//write the whole jpg_buff to the avi file 
	if (fwrite(jpg_buff, jpg_length, 1, avifile) != 1)
		ret = -2;

	if (remnant > 0) 
	{
      if (fwrite(jpg_buff, remnant, 1, avifile) != 1)
		  ret = -2;
    }

	avi_data->frame_sum++;
	avi_data->jpg_sz_64 +=jpg_length_align;
	return ret;

}

//void avi_create(FILE * avifile, Avi_Data *avi_data, unsigned int avi_fps,unsigned int avi_width,unsigned int avi_height)
//{
//	//DWORD riff_sz=0;
//	unsigned int riff_sz=0;
//	unsigned int nbr=0;
//	off64_t riff_sz_64 = 0;
//	unsigned int jpg_sz = 1;
//	unsigned int frames = 1;
//
//	unsigned int per_usec = 1;
//    unsigned int width=0;
//    unsigned int height=0;
//    unsigned int fps=1;
//	List * f;
//
//	struct AVI_list_hdrl hdrl = {
//    /* header */
//    {
//      {'L', 'I', 'S', 'T'},
//      LILEND4(sizeof(struct AVI_list_hdrl) - 8),
//      {'h', 'd', 'r', 'l'}
//    },
//
//    /* chunk avih */
//    {'a', 'v', 'i', 'h'},
//    LILEND4(sizeof(struct AVI_avih)),
//    {
//      LILEND4(per_usec),
//      LILEND4(1000000 * (jpg_sz/frames) / per_usec),
//      LILEND4(0),
//      LILEND4(AVIF_HASINDEX),
//      LILEND4(frames),
//      LILEND4(0),
//      LILEND4(1),
//      LILEND4(0),
//      LILEND4(width),
//      LILEND4(height),
//      {LILEND4(0), LILEND4(0), LILEND4(0), LILEND4(0)}
//    },
//
//    /* list strl */
//    {
//      {
//	{'L', 'I', 'S', 'T'},
//	LILEND4(sizeof(struct AVI_list_strl) - 8),
//	{'s', 't', 'r', 'l'}
//      },
//
//      /* chunk strh */
//      {'s', 't', 'r', 'h'},
//      LILEND4(sizeof(struct AVI_strh)),
//      {
//	{'v', 'i', 'd', 's'},
//	{'M', 'J', 'P', 'G'},
//	LILEND4(0),
//	LILEND4(0),
//	LILEND4(0),
//	LILEND4(per_usec),
//	LILEND4(1000000),
//	LILEND4(0),
//	LILEND4(frames),
//	LILEND4(0),
//	LILEND4(0),
//	LILEND4(0)
//      },
//      
//      /* chunk strf */
//      {'s', 't', 'r', 'f'},
//      sizeof(struct AVI_strf),
//      {      
//	LILEND4(sizeof(struct AVI_strf)),
//	LILEND4(width),
//	LILEND4(height),
//	LILEND4(1 + 24*256*256),
//	{'M', 'J', 'P', 'G'},
//	LILEND4(width * height * 3),
//	LILEND4(0),
//	LILEND4(0),
//	LILEND4(0),
//	LILEND4(0)
//      },
//
//      /* list odml */
//      {
//	{
//	  {'L', 'I', 'S', 'T'},
//	  LILEND4(16),
//	  {'o', 'd', 'm', 'l'}
//	},
//	{'d', 'm', 'l', 'h'},
//	LILEND4(4),
//	LILEND4(frames)
//      }
//    }
//  };	
//
//  	fps = avi_fps;
//	width = avi_width;
//	height = avi_height;
//	
//  //compute how many frames have been record 
//	if((frames = list_size(avi_data->frlst)) == 0)
//	{
//		goto goback;
//	}
//	if(frames !=  avi_data->frame_sum)
//	{
//		goto goback;
//	}
//
//	//printf("\navi_create(): frames=%d",frames);
//
//	/* indices size = 8 + 16*frames */
//	fwrite("idx1", 4, 1, avifile);
//	print_quartet(16 * frames,avifile);
//	for (f = avi_data->frlst; (f); f = f->next) 
//	{
//		/*printf("00db");*/
//		fwrite("00db", 4, 1, avifile);
//		print_quartet(18,avifile);
//		print_quartet(((Jpeg_Data *) f->data)->offset,avifile);
//		print_quartet(((Jpeg_Data *) f->data)->size,avifile);
//	}
//
//	//avi head infomation
//	//  jpg_sz = (long) jpg_sz_64;
//	jpg_sz = (unsigned int) avi_data->jpg_sz_64;
//
//	riff_sz_64 = sizeof(struct AVI_list_hdrl) + 4 + 4 + avi_data->jpg_sz_64
//					+ 8*frames + 8 + 8 + 16*frames;
//	//riff_sz = (DWORD) riff_sz_64;
//	riff_sz = (unsigned int) riff_sz_64;
//	//printf("\nriff_sz=%d",riff_sz);
//
//  /* printing AVI riff hdr*/
//  //return to the beginning of avi file !!!
//	fseek(avifile,0,SEEK_SET);
//
//	fwrite("RIFF", 4, 1, avifile);
//	print_quartet(riff_sz,avifile);
//	fwrite("AVI ", 4, 1, avifile);
//
//	if(0==fps)
//	{
//		per_usec = 1;
//	}
//	else
//	{
//		per_usec = 1000000 / fps;
//	}
//	// list hdrl 
//	hdrl.avih.us_per_frame = LILEND4(per_usec);
//	//hdrl.avih.max_bytes_per_sec = LILEND4(1000000 * (jpg_sz/frames) / per_usec);
//	hdrl.avih.max_bytes_per_sec = LILEND4((jpg_sz/frames)*fps);
//	hdrl.avih.tot_frames = LILEND4(frames);
//	hdrl.avih.width = LILEND4(width);
//	hdrl.avih.height = LILEND4(height);
//	hdrl.strl.strh.scale = LILEND4(per_usec);
//	hdrl.strl.strh.rate = LILEND4(1000000);
//	hdrl.strl.strh.length = LILEND4(frames);
//	hdrl.strl.strf.width = LILEND4(width);
//	hdrl.strl.strf.height = LILEND4(height);
//	hdrl.strl.strf.image_sz = LILEND4(width * height * 3);
//	hdrl.strl.list_odml.frames = LILEND4(frames);	
//
//	if((nbr=fwrite(&hdrl, 1, sizeof(hdrl), avifile)) != sizeof(hdrl))
//	{
//        fprintf(stderr, "\ncouldn't write head info to avi file!\n");
//		//printf("sizeof(hdrl)=%d,nbr=%d\n",sizeof(hdrl),nbr);
//		goto goback;
//	}
//  //printf("\nsizeof(hdrl)=%d,nbr=%d\n",sizeof(hdrl),nbr);
//
//  // list movi 
//  //fwrite("LIST", 4, 1, avifile);
//	if((nbr=fwrite("LIST", 1, 4, avifile)) != 4)
//	{
//        fprintf(stderr, "\ncouldn't write head info to avi file!");
//		printf("nbr=%d\n",nbr);
//		goto goback;
//	}
//	print_quartet(jpg_sz + 8*frames + 4,avifile);
//  //fwrite("movi", 4, 1, avifile);
//	if((nbr=fwrite("movi", 1, 4, avifile)) != 4)
//	{
//        fprintf(stderr, "\ncouldn't write head info to avi file!");
//		printf("nbr=%d\n",nbr);
//		goto goback;
//	}
//
//goback:
//  //printf("\n! Begin to free the frame list, frlst=%p",frlst);
//	list_rerased(avi_data->frlst);
//	//printf("\nDone! free the frame list");
//	avi_data->frame_sum=0;
//	avi_data->jpg_sz_64=0;
//}

void avi_create(FILE * avifile, Avi_Data *avi_data, unsigned int avi_fps,unsigned int avi_width,unsigned int avi_height,int avi_type,int rr)
{
	//DWORD riff_sz=0;
	unsigned int riff_sz=0;
	unsigned int nbr=0;
	off64_t riff_sz_64 = 0;
	unsigned int jpg_sz = 1;
	unsigned int frames = 1;

	unsigned int per_usec = 1;
    unsigned int width=0;
    unsigned int height=0;
    unsigned int fps=1;
	List * f;
	fpos_t pos;


	struct AVI_list_hdrl hdrl = 
	{
		// list_hdr
		{
			{'L', 'I', 'S', 'T'},
			LILEND4(sizeof(struct AVI_list_hdrl) - 8),
			{'h', 'd', 'r', 'l'}
		},

		// unsigned char avih_id[4]
		{'a', 'v', 'i', 'h'},
		// DWORD avih_sz;
		LILEND4(sizeof(struct AVI_avih)),
		// struct AVI_avih avih;
		{
			LILEND4(per_usec),
			LILEND4(1000000 * (jpg_sz/frames) / per_usec),
			LILEND4(0),
			LILEND4(AVIF_HASINDEX),
			LILEND4(frames),
			LILEND4(0),
			LILEND4(1),
			LILEND4(0),
			LILEND4(width),
			LILEND4(height),
			{
				LILEND4(0), LILEND4(0), LILEND4(0), LILEND4(0)
			}
		},

		// struct AVI_list_strl strl;
		{
			// list_hdr
			{
				{'L', 'I', 'S', 'T'},
				LILEND4(sizeof(struct AVI_list_strl) - 8),
				{'s', 't', 'r', 'l'}
			},

			// strh_id
			{'s', 't', 'r', 'h'},
			
			// strh_sz
			LILEND4(sizeof(struct AVI_strh)),
			
			// strh
			{
				{'v', 'i', 'd', 's'},
				{'H', '2', '6', '4'},//////////////////////////////////////////////////////////////////////MJPG
				LILEND4(0),
				LILEND4(0),
				LILEND4(0),
				LILEND4(per_usec),
				LILEND4(1000000),
				LILEND4(0),
				LILEND4(frames),
				LILEND4(0),
				LILEND4(0),
				LILEND4(0)
			},
	      
			// strf_id
			{'s', 't', 'r', 'f'},
			// strf_sz
			sizeof(struct AVI_strf),
	      
			// strf_sz
			{      
				LILEND4(sizeof(struct AVI_strf)),
				LILEND4(width),
				LILEND4(height),
				LILEND4(1 + 24*256*256),
				//{'M', 'J', 'P', 'G'},//for jpeg
				{'H', '2', '6', '4'},//mod for mpeg-4//////////////////////////////////////////////////////divx
				LILEND4(width * height * 3),
				LILEND4(0),
				LILEND4(0),
				LILEND4(0),
				LILEND4(0)
			},

		// strf
			{
				{
					{'L', 'I', 'S', 'T'},
					LILEND4(16),
					{'o', 'd', 'm', 'l'}
				},
				{'d', 'm', 'l', 'h'},
				LILEND4(4),
				LILEND4(frames)
			}
		}
	};	

	fgetpos(avifile,&pos);
  	fps = avi_fps;
	width = avi_width;
	height = avi_height;
	
  //compute how many frames have been record 
	f = avi_data->frlst;
	if((frames = list_size(f)) == 0)
	{
		goto goback;
	}
	if(frames !=  avi_data->frame_sum)
	{
		goto goback;
	}

	//printf("\navi_create(): frames=%d",frames);

	/* indices size = 8 + 16*frames */
	fwrite("idx1", 4, 1, avifile);
	print_quartet(16 * frames,avifile);
	for (f = avi_data->frlst; (f); f = f->next) 
	{
		/*printf("00db");*/
		fwrite("00db", 4, 1, avifile);
		print_quartet(18,avifile);
		print_quartet(((Jpeg_Data *) f->data)->offset,avifile);
		print_quartet(((Jpeg_Data *) f->data)->size,avifile);
	}

	//avi head infomation
	//  jpg_sz = (long) jpg_sz_64;
	jpg_sz = (unsigned int) avi_data->jpg_sz_64;

	riff_sz_64 = sizeof(struct AVI_list_hdrl) + 4 + 4 + avi_data->jpg_sz_64
					+ 8*frames + 8 + 8 + 16*frames;
	//riff_sz = (DWORD) riff_sz_64;
	riff_sz = (unsigned int) riff_sz_64;
	//printf("\nriff_sz=%d",riff_sz);

  /* printing AVI riff hdr*/
  //return to the beginning of avi file !!!
	fseek(avifile,0,SEEK_SET);

	fwrite("RIFF", 4, 1, avifile);
	print_quartet(riff_sz,avifile);
	fwrite("AVI ", 4, 1, avifile);

	if(0==fps)
	{
		per_usec = 1;
	}
	else
	{
		per_usec = 100000000 / fps;
	}
	// list hdrl 
	hdrl.avih.us_per_frame = LILEND4(per_usec);
	//hdrl.avih.max_bytes_per_sec = LILEND4(1000000 * (jpg_sz/frames) / per_usec);
	hdrl.avih.max_bytes_per_sec = LILEND4((jpg_sz/frames)*fps);
	hdrl.avih.tot_frames = LILEND4(frames);
	hdrl.avih.width = LILEND4(width);
	hdrl.avih.height = LILEND4(height);
	hdrl.strl.strh.scale = LILEND4(per_usec);
	hdrl.strl.strh.rate = LILEND4(1000000);
	hdrl.strl.strh.length = LILEND4(frames);
	hdrl.strl.strf.width = LILEND4(width);
	hdrl.strl.strf.height = LILEND4(height);
	hdrl.strl.strf.image_sz = LILEND4(width * height * 3);
	hdrl.strl.list_odml.frames = LILEND4(frames);	

	if(avi_type==0)//0:jpeg
	{
		strncpy(hdrl.strl.strf.compression,"H264",4);/////////////////////////////////////////////////////////MJPG
	//	strncpy(hdrl.strl.strh.handler,"MJPG",4);
	}
	else
	{
   	//	strncpy(hdrl.strl.strf.compression,"divx",4);
		strncpy(hdrl.strl.strh.handler,"H264",4);/////////////////////////////////////////////////////////////divx
	}
	if((nbr=fwrite(&hdrl, 1, sizeof(hdrl), avifile)) != sizeof(hdrl))
	{
        fprintf(stderr, "\ncouldn't write head info to avi file!\n");
		//printf("sizeof(hdrl)=%d,nbr=%d\n",sizeof(hdrl),nbr);
		goto goback;
	}
  //printf("\nsizeof(hdrl)=%d,nbr=%d\n",sizeof(hdrl),nbr);

  // list movi 
  //fwrite("LIST", 4, 1, avifile);
	if((nbr=fwrite("LIST", 1, 4, avifile)) != 4)
	{
        fprintf(stderr, "\ncouldn't write head info to avi file!");
		printf("nbr=%d\n",nbr);
		goto goback;
	}
	print_quartet(jpg_sz + 8*frames + 4,avifile);
  //fwrite("movi", 4, 1, avifile);
	if((nbr=fwrite("movi", 1, 4, avifile)) != 4)
	{
        fprintf(stderr, "\ncouldn't write head info to avi file!");
		printf("nbr=%d\n",nbr);
		goto goback;
	}
	fflush(avifile);
goback:
	if(rr)
	{
	  //printf("\n! Begin to free the frame list, frlst=%p",frlst);
		list_rerased(avi_data->frlst);
		//printf("\nDone! free the frame list");
		avi_data->frame_sum=0;
		avi_data->jpg_sz_64=0;
	}
	else
	{
		fseek(avifile,pos,SEEK_SET);
	}
}

void avi_close(FILE * avifile)
{
   /*2005-11-03 close the avi file*/
  fclose(avifile);

}

void * avi_read(FILE * avifile, unsigned int frame_number, unsigned int * jpg_size){

	int frame_amount;
	int fn;
	unsigned int frame_offset=0;
	unsigned int frame_size=0;
	char * jpg_buffer;
	unsigned int nbr;

	fseek(avifile,0x30,SEEK_SET);
	fread(&frame_amount,4,1,avifile);
	fn = frame_number % frame_amount;
	//printf("\n!!!avifile=%p,frame_amount=%d,fn=%d",avifile,frame_amount,fn);

	fseek(avifile,-(16*(frame_amount-fn)-8),SEEK_END);
	fread(&frame_offset,4,1,avifile);
	fread(&frame_size,4,1,avifile);

	//printf("\n!!! frame_offset=%d,frame_size=%d",frame_offset,frame_size);

	if((jpg_buffer=(char *)malloc(frame_size))==NULL){
		fprintf(stderr, "couldn't malloc buffer  !\n");
		return NULL;
	}
    fseek(avifile,244+frame_offset,SEEK_SET);

	if((nbr=fread(jpg_buffer,1,frame_size,avifile))!= frame_size){
		printf("\nnbr=%d",nbr);
	    fprintf(stderr, "\ncouldn't read frame to jpg buffer !\n");
		return NULL;
	}
	//printf("\nnbr=%d,jpg_buffer=%p",nbr,jpg_buffer);

	*jpg_size=frame_size;
	return jpg_buffer;
}